use std::{
    collections::HashMap,
    io::{Cursor, Write},
};
use serde_json::Value;
use shq_common::prelude::{
    options::Options::*,
    report::{get_special_row_props, SpecialRowData, SpecialRows},
    ColumnCells, ControlType, ExcelWriter, Parameter,
};
use zip::write::FileOptions;
pub(crate) async fn export_muliti_xlsx_zip(
    title: &str,
    header_cells: ColumnCells,
    conf_params: &[Parameter],
    params: &[(&str, &str)],
    datas: Vec<Value>,
    sheet_col_name: &str,
    special_rows: Option<&SpecialRows>,
) -> anyhow::Result<Vec<u8>> {
    let col_index = header_cells
        .get_position(sheet_col_name)
        .ok_or(anyhow::anyhow!(
            "export_muliti_xlsx_zip配置错误，sheet_col_name:{}没有找到",
            sheet_col_name
        ))?;
    let mut data_map: HashMap<String, Vec<Value>> = HashMap::new();
    for data in datas {
        let arr = data.as_array().ok_or(anyhow::anyhow!("数据不是数组"))?;
        let sheet_name = arr.get(col_index).and_then(|v| v.as_str());
        if sheet_name.is_none() {
            continue;
        }
        let sheet_name = sheet_name.unwrap();
        data_map
            .entry(sheet_name.to_string())
            .or_default()
            .push(data);
    }
    let mut zip_buffer = Vec::new();
    let mut zip = zip::ZipWriter::new(Cursor::new(&mut zip_buffer));
    let base_options: FileOptions<'_, ()> = FileOptions::default()
        .compression_method(zip::CompressionMethod::Stored)
        .unix_permissions(0o755);
    for (k, v) in data_map.into_iter() {
        let file_name = format!("{}.xlsx", k);
        let bytes = export_xlsx(
            title,
            header_cells.clone(),
            conf_params,
            params,
            v.clone(),
            None,
            special_rows,
        )
        .await?;
        zip.start_file(file_name, base_options)?;
        zip.write_all(&bytes)?;
    }
    zip.finish()?;
    Ok(zip_buffer)
}

pub(crate) async fn export_xlsx(
    title: &str,
    header_cells: ColumnCells,
    conf_params: &[Parameter],
    params: &[(&str, &str)],
    datas: Vec<Value>,
    sheet_col_name: Option<&str>,
    special_rows: Option<&SpecialRows>,
) -> anyhow::Result<Vec<u8>> {
    let bdatas = if let Some(cname) = sheet_col_name {
        let idx = header_cells
            .get_position(cname)
            .ok_or(anyhow::anyhow!("sheet_column:{}没有找到", cname))?;
        let mut ew = ExcelWriter::new(header_cells.clone())?;
        let sheet_datas = to_sheet_datas(idx, datas)?;
        for (sheet_name, sheet_data) in sheet_datas.into_iter() {
            let (special_row_datas,datas2) = get_special_row_datas(&header_cells, sheet_data, special_rows)?;
            export_data_by_writer(title, conf_params, params, datas2, &sheet_name, special_row_datas, &mut ew).await?;
        }
        ew.to_bytes()?
    } else {
        let (special_row_datas,datas2) = get_special_row_datas(&header_cells, datas, special_rows)?;
        let mut ew = ExcelWriter::new(header_cells)?;
        let sheet_name = "Sheet1";
        export_data_by_writer(title, conf_params, params, datas2, sheet_name, special_row_datas,&mut ew).await?;
        ew.to_bytes()?
    };
    Ok(bdatas)
}
fn get_special_row_datas(
    header_cells: &ColumnCells,
    datas: Vec<Value>,
    special_rows: Option<&SpecialRows>,
) -> anyhow::Result<(Vec<(usize, SpecialRowData)>, Vec<Value>)> {
    let mut special_row_datas: Vec<(usize, SpecialRowData)> = Vec::new();
    let mut datas2: Vec<Value> = Vec::with_capacity(datas.len());
    for (row_idx, row) in datas.into_iter().enumerate() {
        match row {
            Value::Array(arr) => {
                if let Some(sr) = special_rows {
                    let (special_row, row_data) = get_special_row_props(&header_cells, arr, sr);
                    if let Some(sr) = special_row {
                        special_row_datas.push((row_idx, sr));
                    }
                    datas2.push(Value::Array(row_data));
                }else{
                    datas2.push(Value::Array(arr));
                }
            }
            _ => anyhow::bail!("数据不是数组"),
        }
    }
    Ok((special_row_datas, datas2))
}
fn to_sheet_datas(
    sheet_col_index: usize,
    datas: Vec<Value>,
) -> anyhow::Result<Vec<(String, Vec<Value>)>> {
    let mut sheet_datas: Vec<(String, Vec<Value>)> = Vec::new();
    for data in datas.into_iter() {
        let arr = data.as_array().ok_or(anyhow::anyhow!("数据不是数组"))?;
        let sheet_name = arr.get(sheet_col_index).and_then(|v| v.as_str());
        if sheet_name.is_none() {
            continue;
        }
        let sname = sheet_name.unwrap().to_string();
        let idx = sheet_datas.iter().position(|(s, _)| s == &sname);
        if let Some(idx) = idx {
            sheet_datas[idx].1.push(data);
        } else {
            sheet_datas.push((sname, Vec::new()));
            let idx = sheet_datas.len() - 1;
            sheet_datas[idx].1.push(data);
        }
    }
    Ok(sheet_datas)
}
async fn export_data_by_writer(
    title: &str,
    conf_params: &[Parameter],
    params: &[(&str, &str)],
    datas: Vec<Value>,
    sheet_name: &str,
    special_row_datas: Vec<(usize, SpecialRowData)>,
    ew: &mut ExcelWriter,
) -> anyhow::Result<Vec<u8>> {
    let mut sub_title = String::new();
    for (key, val) in params.iter() {
        for p in conf_params.iter() {
            if p.name == *key {
                let label_val = if p.edit_type == ControlType::Select {
                    if let Some(options) = p.options.as_ref() {
                        match options {
                            Array(_) => *val,
                            Map(kv_items) => {
                                let label = kv_items
                                    .iter()
                                    .find(|item| item.value.as_str() == *val)
                                    .map(|f| f.label.as_str())
                                    .unwrap_or("");
                                label
                            }
                            Tree(tree_nodes) => {
                                let label = tree_nodes
                                    .iter()
                                    .find(|node| node.key.as_str() == *val)
                                    .map(|n| n.title.as_str())
                                    .unwrap_or("");
                                label
                            }
                            Fetch(_) => *val,
                        }
                    } else {
                        *val
                    }
                } else {
                    *val
                };
                sub_title.push_str(&format!("{}:{} ", p.caption, label_val));
            }
        }
    }
    let sub_title = if sub_title.is_empty() {
        None
    } else {
        Some(sub_title.as_str())
    };
    ew.create_header(sheet_name, false, Some(title), sub_title)?;
    ew.create_body(sheet_name, datas, special_row_datas)?;
    let datas = ew.to_bytes()?;
    Ok(datas)
}
